 /* ********************************************************************************
  *    Copyright:  (C) 2023 LN
  *    All rights reserved.
  *
  *       Filename:  socket_client.c
  *    Description:  This file 
  *                 
  *      Version:  1.0.0(2023年04月05日)
  *       Author:  Li Haipeng <NULL>
  *    ChangeLog:  1, Release initial version on "2023年04月05日 23时38分38秒"
  *                    
  *
  ********************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/epoll.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include "socket_client.h"
#include "logger.h"
#include "pack_data.h"



int socket_connect(socket_t *sock)
{
    int                      		 rv = -1;
    int                      		 i;
	struct	hostent					*gethost = NULL;
	extern	int			  	 		 h_errno;
	struct	in_addr		 	 		**addr_list;
	struct	sockaddr_in		 		 addr_in;
	static struct sockaddr_in        servaddr;


	if( !sock )
	{
		return 0;
	}

	sock->fd = -1;
	sock->connected = 0;

    sock->fd = socket(AF_INET, SOCK_STREAM, 0);
    if(sock->fd < 0 )
    {
        log_error("Create socket failure:%s", strerror(errno));
        return 0;
    }
    log_debug("Create socket[%d] successfully!", sock->fd );

	
    if(inet_aton(sock->host, &servaddr.sin_addr))
	{
		log_debug("This is a IP!");

    	memset(&servaddr, 0, sizeof(servaddr));
    	servaddr.sin_family = AF_INET;
		servaddr.sin_port = htons(sock->port);
    	inet_aton(sock->host, &servaddr.sin_addr);


    	rv = connect(sock->fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    	if(rv < 0)
    	{
       		log_error("Connect to server[%s:%d] failure:%s",sock->host, sock->port, strerror(errno));
			return 0;
    	}
		log_debug("Connect to server[%s:%d] successfully!:", sock->host, sock->port);

		sock->connected = 1;

		return 1;
			
	}
	else
	{
		gethost = gethostbyname(sock->host);
		if(gethost == NULL)
		{
			log_error("%s", hstrerror(h_errno));
			return 0;
		}

		addr_list = (struct in_addr **)gethost->h_addr_list;
		for(i = 0; addr_list[i] != NULL; i++)
		{
			memset(&sock->host, 0, sizeof(sock->host));
			strncpy(sock->host, inet_ntoa(*addr_list[i]), sizeof(sock->host));
	

    		memset(&servaddr, 0, sizeof(servaddr));
    		servaddr.sin_family = AF_INET;
			servaddr.sin_port = htons(sock->port);
    		inet_aton(sock->host, &servaddr.sin_addr);


    		rv = connect(sock->fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    		if(rv < 0)
    		{
        		log_error("Connect to server[%s:%d] failure:%s",sock->host, sock->port, strerror(errno));
        		continue;                    
    		}
			log_debug("Connect to server[%s:%d] successfully!:", sock->host, sock->port);

			sock->connected = 1;
			
   			return 1;
		}
        return 0;
	}
}


int socket_write(socket_t *sock, char *data, int bytes)
{
    int     rv = -1;
    int		len = 0;
    

	if( !sock )
	{
		return 0;
	}

	while( bytes > 0 )
	{
		len = bytes > 512 ? 512 : bytes;

    	rv = write(sock->fd, data, len);
    	if(rv < 0)    
   		{
        	log_error("Write to server by sockfd[%d] failure:%s", sock->fd, strerror(errno));
    		return 0;
    	}
		bytes -= rv;
		data += rv;
	}
    

    return 1;
}

int socket_data_send(socket_t *sock,packet_t *pack)
{
	char	buf[1024];
	int		rv = 0;

    if( !sock || !pack )
	{
		return 0;
	}

    
	memset(buf, 0, sizeof(buf));
	pack_data(pack, buf, sizeof(buf));

	rv = socket_write(sock, buf, sizeof(buf));
	if( !rv )
	{
		log_error("Falied to send data!");
		return 0;
	}

	return 1;
}

int socket_diag(socket_t *sock)
{
    struct tcp_info     info;
    int     			len = sizeof(info);


	if( !sock )
	{
		return 0;
	}

	getsockopt(sock->fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
	if(info.tcpi_state == TCP_ESTABLISHED)
	{
		sock->connected = 1;
		log_debug("socket successfully!");
		return 1;
	}
	else
	{
		sock->connected = 0;
		log_debug("socket faliure!");
		return 0;
	}
}


int socket_close(socket_t *sock)
{
	if( !sock )
	{
		return 0;
	}

	close(sock->fd);
	sock->fd = -1;
	sock->connected = 0;
}

int socket_server_init(socket_t *sock)
{
	int                     		rv = 0;
	int                     		on = 1; 
	int                     		listenfd;
	static struct sockaddr_in       servaddr;


	if( !sock )
	{
		return -1;
	}

    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));
        return -2;
    }

    setsockopt(listenfd , SOL_SOCKET, SO_REUSEADDR , &on , sizeof(on));

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(sock->port);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if( !strcmp(sock->host,"hello") )
    {
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	}
	else
	{
	    if(inet_pton(AF_INET, sock->host, &servaddr.sin_addr) <= 0)
	    {
	        printf("inet_pton() set listen IP address failure.\n");
	        rv = -2;
	        goto CleanUp;
	    }
	}

    if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {
        printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
	    return -3;
	    goto CleanUp;
	}

	if(listen(listenfd, 200) < 0)
	{
		 printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
		 return -4;
		 goto CleanUp;
	}
	    
CleanUp:
	if(rv < 0) 
		close(listenfd);
	else      	
		rv = listenfd;
	return rv;
}

void set_socket_rlimit(void)
{
    struct rlimit limit = {0};
 	
	getrlimit(RLIMIT_NOFILE, &limit);
	limit.rlim_cur = limit.rlim_max;
	setrlimit(RLIMIT_NOFILE ,&limit);		

	printf("set socket open fd max count to %d\n", limit.rlim_max);

}
